<?php

namespace app\common\model;

use app\common\model\recharge\Order as ModelOrder;
use app\common\model\shop\Order;
use fast\Http;
use think\Env;
use think\Model;
use wxpay\Wxpayapi;
use wxpay\WxPayConfig;

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use WeChatPay\Formatter;
use GuzzleHttp\Middleware;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;


// require_once(EXTEND_PATH . "/wxpay/WxPayData.php");

/**
 * 邮箱验证码
 */
class Coreorder extends Model
{


    // 表名
    protected $name = 'core_order';

    // 自动写入时间戳字段
    protected $autoWriteTimestamp = 'int';

    // 定义时间戳字段名
    protected $createTime = 'createtime';
    protected $updateTime = 'updatetime';
    // 追加属性
    protected $append = [];

    //生成订单编号
    static function getorderno()
    {
        $order_no = date('YmdHis') . substr(uniqid(), 6);
        return $order_no;
    }

    //根据订单获取微信支付参数
    static function payorder($order_info, $openid)
    {

        $wxmchid = config('site.wxmchid');
        $wxkey = config('site.wxkey');
        $wxappid = config('site.appid');
        $wxappsecret = config('site.appsecret');
        if (!$wxmchid || !$wxkey || !$wxappid || !$wxappsecret) {
            $result = array('code' => 3, 'msg' => ('微信配置不完整'));
            return $result;
        }
        $payfees = $order_info->fee;
        //为0直接支付成功
        if ($order_info->fee == 0) {
            self::notifyOK($order_info->id);
            $result = array('code' => 2, 'msg' => ('已成功支付'));
        }
        $pay_total = sprintf('%.2f', $order_info->fee) * 100;
        // 调用支付宝进行支付
        $config = array(
            'APPID' => $wxappid,
            'MCHID' => $wxmchid,
            'WXKEY' => $wxkey,
            'APPSECRET' => $wxappsecret,
            // 'ip'=>Helper_Common::get_ip(),
        );
        $data = array(
            'user_id' => $order_info->user_id,
            'payfee' => $payfees
        );
        // $payment_sn = payment::createpayment($order_info);
        $payment_sn = $order_info->order_no;

        WxPayConfig::setconfig($wxmchid, $wxkey, $wxappid, $wxappsecret);
        $body = '微信在线支付';
        $url = getRequestHost();
        $notityUrl = $url . "api/wxpay/notify";
        $trade_type = 'JSAPI';
        //$Device_info='WEB';
        //给需要提交的必须参数赋值
        $wxpay = new \wxpay\WxPayUnifiedOrder();
        //$wxpay->SetDevice_info($Device_info);
        $wxpay->SetBody($body);
        $wxpay->SetOut_trade_no($payment_sn);
        $wxpay->SetTotal_fee($pay_total);
        $wxpay->SetNotify_url($notityUrl);
        $wxpay->SetTrade_type($trade_type);
        $wxpay->SetAttach(json_encode($data));
        $wxpay->SetOpenid($openid);
        $order =  WxPayApi::unifiedOrder($wxpay, $config);
        if (empty($order['prepay_id'])) {
            if ($order['return_code'] == 'FAIL') {
                return array('code' => 1, 'msg' => $order['return_msg']);
            } else {
                return array('code' => 10000, 'msg' => $order['err_code_des'] ?? '未知错误');
            }
        }
        $order_info->prepay_id = $order['prepay_id'];
        $order_info->save();
        $arr = array();
        $arr['appId'] = $order["appid"];
        $arr['nonceStr'] = $order["nonce_str"];
        $arr['package'] = "prepay_id=" . $order['prepay_id'];
        $arr['signType'] = "MD5";
        $arr['timeStamp'] = (string)time();
        $str = self::ToUrlParams($arr);
        $jmstr = $str . "&key=" . $wxkey;
        $arr['paySign'] = strtoupper(MD5($jmstr));
        $arr['notityUrl'] = $notityUrl;
        $arr['notis'] = $data;
        return $arr;
    }

    static function apppayorder($order_info)
    {

        $wxmchid = Env::get('wx.wxmchid');
        $wxkey =  Env::get('wx.wxkey');
        $wxappid =  Env::get('wx.appid');
        $wxappsecret =  Env::get('wx.appsecret');
        if (!$wxmchid || !$wxkey || !$wxappid || !$wxappsecret) {
            $result = array('code' => 3, 'msg' => ('微信配置不完整'));
            return $result;
        }
        $payfees = $order_info->fee;
        //为0直接支付成功
        if ($order_info->fee == 0) {
            self::notifyOK($order_info->id);
            $result = array('code' => 2, 'msg' => ('已成功支付'));
        }
        $pay_total = sprintf('%.2f', $order_info->fee) * 100;
        // 调用支付宝进行支付
        $config = array(
            'APPID' => $wxappid,
            'MCHID' => $wxmchid,
            'WXKEY' => $wxkey,
            'APPSECRET' => $wxappsecret,
            // 'ip'=>Helper_Common::get_ip(),
        );
        $data = array(
            'user_id' => $order_info->user_id,
            'payfee' => $payfees
        );
        // $payment_sn = payment::createpayment($order_info);
        $payment_sn = $order_info->order_no;

        WxPayConfig::setconfig($wxmchid, $wxkey, $wxappid, $wxappsecret);
        $body = '微信在线支付';
        $url = getRequestHost();
        $notityUrl = $url . "api/wxpay/notify";
        $trade_type = 'APP';
        //$Device_info='WEB';
        //给需要提交的必须参数赋值
        $wxpay = new \wxpay\WxPayUnifiedOrder();
        //$wxpay->SetDevice_info($Device_info);
        $wxpay->SetBody($body);
        $wxpay->SetOut_trade_no($payment_sn);
        $wxpay->SetTotal_fee($pay_total);
        $wxpay->SetNotify_url($notityUrl);
        $wxpay->SetTrade_type($trade_type);
        $wxpay->SetAttach(json_encode($data));
        // $wxpay->SetOpenid($openid);
        $order =  WxPayApi::unifiedOrder($wxpay, $config);
        if (empty($order['prepay_id'])) {
            if ($order['return_code'] == 'FAIL') {
                return array('code' => 1, 'msg' => $order['return_msg']);
            } else {
                return array('code' => 10000, 'msg' => $order['err_code_des'] ?? '未知错误');
            }
        }
        $order_info->prepay_id = $order['prepay_id'];
        $order_info->save();
        $arr = array();
        $arr['appid'] = $order["appid"];
        $arr['noncestr'] = $order["nonce_str"];
        // $arr['package'] = "prepay_id=" . $order['prepay_id'];
        $arr['prepay_id'] = $order['prepay_id'];
        $arr['timestamp'] = (string)time();
        $str = self::ToUrlParams($arr);
        $arr['signType'] = "MD5";
        $jmstr = $str . "&key=" . $wxkey;
        $arr['sign'] = strtoupper(MD5($jmstr));
        $arr['package'] = "Sign=WXPay";
        $arr['notityUrl'] = $notityUrl;
        $arr['notis'] = $data;
        return $arr;
    }
    //微信支付签名串排序
    static function ToUrlParams($urlObj)
    {
        $buff = "";
        foreach ($urlObj as $k => $v) {
            if ($k != "sign") {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    //支付完成回调
    static function notifyOK($id)
    {
        $coreorder = self::get($id);
        //修改支付状态
        if ($coreorder->status == 0) {
            $coreorder->status = '1';
            $coreorder->save();

            switch ($coreorder->module_mark) {

                case 'shop':
                    // $uplevel_checktag = false;
                    Order::payok($coreorder->order_id);
                    # code...
                    break;
                case 'vip':
                    // $uplevel_checktag = false;
                    Viporder::payok($coreorder->order_id);
                    # code...
                    break;
                case 'recharge':
                    ModelOrder::payok($coreorder->order_id);
                    break;
                default:
                    # code...
                    break;
            }
        }
    }

    /**
     * 退款
     * @Author   xy
     * @DateTime 2019-08-30T15:17:05+0800
     * @param    [type]                   $order_id [description]
     * @param    [type]                   $price    [description]
     * @return   [type]                             [description]
     */
    static function refund($order, $price)
    {
        $return = array("code" => -1, "msg" => "");
        if (empty($price)) {
            $return['msg'] = '金额不能为空';
            return $return;
        }

        $wxmchid = config('site.wxmchid');
        $wxkey = config('site.wxkey');
        $wxappid = config('site.appid');
        $wxappsecret = config('site.appsecret');
        if (!$wxmchid || !$wxkey || !$wxappid || !$wxappsecret) {
            $result = array('code' => 3, 'msg' => ('微信配置不完整'));
            return $result;
        }

        $user = User::where('id', $order->user_id)->find();
        if (!$user) {
            $return['msg'] = '用户不存在';
            return $return;
        }


        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
        $pars = array();
        $pars['appid'] = $wxappid;
        $pars['mch_id'] = $wxmchid;
        $pars['nonce_str'] = get_rand_str(32, false);
        $pars['out_trade_no'] = $order->order_no; //兼容新增payment
        $pars['out_refund_no'] = "R_" . $order->order_no;
        $pars['total_fee'] = $order->fee * 100;
        $pars['refund_fee'] = $price * 100;
        $pars['op_user_id'] = $user->openid;

        ksort($pars, SORT_STRING);
        $string1 = '';

        foreach ($pars as $k => $v) {
            $string1 .= $k . '=' . $v . '&';
        }

        $string1 .= 'key=' . $wxkey;
        $pars['sign'] = strtoupper(md5($string1));
        // $xml = array2xml($pars);
        $extras = array();

        $certfile = ROOT_PATH . '/data/apiclient_cert.pem';
        $keyfile = ROOT_PATH . '/data/apiclient_key.pem';

        $extras['certPath'] = $certfile;
        $extras['keyPath'] = $keyfile;


        $arr = post_data($url, $pars, "xml", true, $extras);


        if ($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS') {
            $return['code'] = 0;
            return $return;
        }

        if ($arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'FAIL' && $arr['return_msg'] == 'OK') {
            if ($arr['err_code'] == 'NOTENOUGH') {
                $return['msg'] = "账号余额不足";
                return $return;
            } else {
                $return['msg'] = $arr['err_code'] . '订单号' . $pars['out_trade_no'];
                return $return;
            }
        } else {
            if (empty($arr['err_code_des']) || $arr['return_msg'] == $arr['err_code_des']) {
                $error = $arr['return_msg'];
            } else {
                $error = $arr['return_msg'] . ' | ' . $arr['err_code_des'];
            }

            $return['msg'] = $error . '订单号' . $pars['out_trade_no'];
            return $return;
        }
    }





    static function refundv3($order, $price)
    {
        $return = array("code" => -1, "msg" => "");
        if (empty($price)) {
            $return['msg'] = '金额不能为空';
            return $return;
        }


        $url = getRequestHost();
        $notityUrl = $url . "api/wxpay/notifyv3";
        $wxmchid = Env::get('wx.wxmchid');
        $wxkey = Env::get('wx.wxkey');
        $wxappid = Env::get('wx.appid');

        if (!$wxmchid || !$wxkey || !$wxappid) {
            $result = array('code' => 3, 'msg' => ('微信配置不完整'));
            return $result;
        }

        $user = User::where('id', $order->user_id)->find();
        if (!$user) {
            $return['msg'] = '用户不存在';
            return $return;
        }

        $pars = [
            'out_trade_no'=>$order->order_no,
            'out_refund_no'=>"R_" . $order->order_no,
            'amount'=>[
                'refund' => $price * 100,
                'total' => $order->fee * 100,
                'currency' => 'CNY'
            ],
            'funds_account'=>'AVAILABLE'
        ];


        $instance = self::getwxpayInstance();
        if (!is_object($instance)) {
            return $instance;
        }

        try {
            $resp = $instance
                ->chain("v3/refund/domestic/refunds")->post([
                    "json" => $pars,
                ]);
            $resp = $resp->getBody();
            $res = json_decode($resp, 1);
            // var_dump($res);
            return ['code' => 0, 'data' => $res];
        } catch (\Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $res = json_decode($r->getBody(), 1);

                // var_dump($res);

                return ['code' => $r->getStatusCode(), 'data' => $res, 'msg' => $res['message']];
            }
            return ['code' => 6001, 'msg' => $e->getMessage()];
        }
    }
    static function transferv3($withdraw)
    {
        $return = array("code" => -1, "msg" => "");
        // if (empty($price)) {
        //     $return['msg'] = '金额不能为空';
        //     return $return;
        // }


        $url = getRequestHost();
        $notityUrl = $url . "api/wxpay/notifyv3";
        $wxmchid = Env::get('wx.wxmchid');
        $wxkey = Env::get('wx.wxkey');
        $wxappid = Env::get('wx.appid');

        if (!$wxmchid || !$wxkey || !$wxappid) {
            $result = array('code' => 3, 'msg' => ('微信配置不完整'));
            return $result;
        }



        $pars = array();
        $pars['appid'] = $wxappid;
        $pars['mch_id'] = $wxmchid;
        $pars['out_batch_no'] = $withdraw->order_no;
        $pars['batch_name'] = '用户提现';
        $pars['batch_remark'] = $withdraw->order_no;
        $pars['total_amount'] = ($withdraw->money - $withdraw->tax) * 100;
        $pars['total_num'] = 1;
        $pars['transfer_detail_list'] = [
            [
                'out_detail_no' => $withdraw->order_no . '001',
                'transfer_amount' => ($withdraw->money - $withdraw->tax) * 100,
                'transfer_remark' => '提现',
                'openid' => $withdraw->openid,
            ]
        ];


        $instance = self::getwxpayInstance();
        if (!is_object($instance)) {
            return $instance;
        }


        try {
            $resp = $instance
                ->chain("v3/transfer/batches")->post([
                    "json" => $pars,
                ]);
            $resp = $resp->getBody();
            $res = json_decode($resp, 1);
            return ['code' => 0, 'data' => $res];
        } catch (\Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $res = json_decode($r->getBody(), 1);
                return ['code' => $r->getStatusCode(), 'data' => $res, 'msg' => $res['message']];
            }
            return ['code' => 6001, 'msg' => $e->getMessage()];
        }
    }

    static function wxapiv3pay($order_info, $type = 'xcx')
    {
        $wxmchid = Env::get('wx.wxmchid');
        $wxkey = Env::get('wx.wxkey');

        $payfees = $order_info->fee;
        //为0直接支付成功
        if ($order_info->fee == 0) {
            self::notifyOK($order_info->id);
            $result = array('code' => 2, 'msg' => ('已成功支付'));
        }

        $instance = self::getwxpayInstance();
        if (!is_object($instance)) {
            return $instance;
        }

        $url = getRequestHost();
        $notityUrl = $url . "api/wxpay/notifyv3";
        if ($type == 'xcx') {
            $json = [
                'appid' => Env::get('wx.appid'),
                'mchid' => $wxmchid,
                'description' => '微信在线支付',

                'out_trade_no'  => $order_info->order_no,
                'notify_url' => $notityUrl,
                'amount'         => [
                    'total'    => intval($order_info->fee * 100),
                    'currency' => 'CNY'
                ],
                'payer' => [
                    'openid' => $order_info->user->openid
                ]
            ];
            return self::wxapipay('jsapi', $json, $instance);
        } elseif ($type == 'app') {
            $json = [
                'appid' => Env::get('wx.appid'),
                'mchid' => $wxmchid,
                'description' => '微信在线支付',

                'out_trade_no'  => $order_info->order_no,
                'notify_url' => $notityUrl,
                'amount'         => [
                    'total'    => intval($order_info->fee * 100),
                    'currency' => 'CNY'
                ]
            ];
            return self::wxapipay('app', $json, $instance);
        } elseif ($type == 'native') {
            $json = [
                'appid' => Env::get('wx.appid'),
                'mchid' => $wxmchid,
                'description' => '微信在线支付',

                'out_trade_no'  => $order_info->order_no,
                'notify_url' => $notityUrl,
                'amount'         => [
                    'total'    => intval($order_info->fee * 100),
                    'currency' => 'CNY'
                ]
            ];
            return self::wxapipay('native', $json, $instance);
        }
    }

    private static function wxapipay($type, $json, $instance)
    {
        try {
            $nonce = Formatter::nonce();
            $timestamp = (string) Formatter::timestamp();
            $merchantPrivateKeyFilePath = 'file://' . ROOT_PATH . 'data/apiclient_key.pem';
            $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

            $remoteSigner = function (RequestInterface $request) use ($merchantPrivateKeyInstance, $nonce, $timestamp): string {
                $signature = Rsa::sign(Formatter::request(
                    $request->getMethod(),
                    $request->getRequestTarget(),
                    $timestamp,
                    $nonce,
                    $request->getBody()
                ), $merchantPrivateKeyInstance);

                // var_dump($signature);
                return $signature;
            };
            $merchantCertificateSerial = Env::get('wx.wxpay_cert_serial');
            $wxmchid = Env::get('wx.wxmchid');

            $stack = $instance->getDriver()->select()->getConfig('handler');
            // 卸载SDK内置签名中间件
            $stack->remove('signer');
            // 注册内网远程请求签名中间件
            $stack->before('prepare_body', Middleware::mapRequest(
                static function (RequestInterface $request) use ($remoteSigner, $timestamp, $nonce, $merchantCertificateSerial, $wxmchid): RequestInterface {
                    return $request->withHeader('Authorization', Formatter::authorization(
                        $wxmchid,
                        $nonce,
                        $remoteSigner($request),
                        $timestamp,
                        $merchantCertificateSerial
                    ));
                }
            ), 'signer');

            $resp = $instance
                ->chain("v3/pay/transactions/{$type}")->post([
                    "json" => $json,

                ]);

            $resp1 = $resp->getBody();

            $res = json_decode($resp1, 1);

            $data = self::getSign($timestamp, $nonce,  $res['prepay_id'], $json, $type);
            return ['code' => 0, 'data' => $data];
        } catch (\Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $res = json_decode($r->getBody(), 1);
                return ['code' => $r->getStatusCode(), 'data' => $res, 'msg' => $res['message']];
            }
            return ['code' => 6001, 'msg' => $e->getMessage()];
        }
    }


    private static function getSign($timestamp, $nonce,  $prepayid, $json, $type)
    {


        $params = [
            'appid' => $json['appid'],
            'timestamp' => $timestamp,
            'noncestr' => $nonce,
            'prepayid' => $prepayid
        ];


        $merchantPrivateKeyFilePath = 'file://' . ROOT_PATH . 'data/apiclient_key.pem';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        $params += ['sign' => Rsa::sign(
            Formatter::joinedByLineFeed(...array_values($params)),
            $merchantPrivateKeyInstance
        ), 'partnerid' => $json['mchid'], 'package' => 'Sign=WXPay'];
        return $params;
    }

    private static function getwxpayInstance()
    {
        $wxmchid = Env::get('wx.wxmchid');
        $wxkey = Env::get('wx.wxkey');
        // $wxappid = config('site.appid');
        // $wxappsecret = config('site.appsecret');
        if (!$wxmchid || !$wxkey) {
            $result = array('code' => 3, 'msg' => ('微信配置不完整'));
            return $result;
        }
        if (!file_exists(ROOT_PATH . 'data/apiclient_key.pem') || !file_exists(ROOT_PATH . 'data/wechatpay.pem')) {
            $result = array('code' => 6, 'msg' => ('微信支付证书未找到'));
            return $result;
        }
        // $config = config('wechat.app_payment.default');
        // $merchantId =  $config['mch_id'];

        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = 'file://' . ROOT_PATH . 'data/apiclient_key.pem';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        // 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = Env::get('wx.wxpay_cert_serial');

        // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        // composer exec CertificateDownloader.php -- -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
        $platformCertificateFilePath = 'file://' . ROOT_PATH . 'data/wechatpay.pem';
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid'      => $wxmchid,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);




        return $instance;
    }
}
